package com.wn.common.net

import androidx.collection.SimpleArrayMap
import com.google.gson.Gson
import com.wn.common.net.config.*
import com.wn.common.net.support.IHttpCallback
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File
import java.io.IOException
import java.util.concurrent.TimeUnit
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

/**
 * @Author: wn
 * @Date: 2023/2/13 16:29
 * @Description: okhttp 实现     应该跟 retrofit 没什么关系
 */
object OkHttpApi : HttpApi {

  private val baseUrl = "http://api.qingyunke.com/"

  // 存储请求，用于取消
  private val cacheCall = SimpleArrayMap<Any, Call>()

  //  创建 OkHttpClient
  private val mClient by lazy {
    OkHttpClient.Builder()
      .callTimeout(10, TimeUnit.SECONDS) // 完整请求超时时长，从发起到接收返回数据，默认值0s，不限定
      .connectTimeout(10, TimeUnit.SECONDS) // 与服务器建立连接的时长，默认--10s  秒
      .readTimeout(10, TimeUnit.SECONDS) // 读取服务器返回数据的时长  s
      .writeTimeout(10, TimeUnit.SECONDS) // 向服务器写入数据的时长，默认--10s
      .retryOnConnectionFailure(true) // 重连
      .followRedirects(false)// 重定向
      .cache(Cache(File("data/user/0/com.wn.caiiao/cache", "okHttp"), 1024))// 本地缓存
      .cookieJar(LocalCookieKar())// 本地 cookies 持久化存储
      /*.addInterceptor(HostInterceptor()) // BaseUrl 拦截*/
      .addNetworkInterceptor(RetryInterceptor(1))// 重试
      .addNetworkInterceptor(CnInterceptor()) // 公共 header的拦截器
      // 添加网络拦截器，可以对okHttp的网络请求做拦截处理，不同于应用拦截器，这里能感知所有网络状态，比如重定向
      .addNetworkInterceptor(HttpLogInterceptor {
        logTag("<wn--OkHttpApi>")
        logLevel(LogLevel.BODY) // 设置 打印所有
      })
      .build()
  }

  /**
   * get异步请求
   * @param params Map<String, Any>  请求参数
   * @param url String               请求地址
   * @param callback IHttpCallback   请求回调
   */
  override fun get(params: Map<String, Any>, url: String, callback: IHttpCallback) {
    val fullUrl = baseUrl + url
    val urlBuilder = fullUrl.toHttpUrl().newBuilder()
    params.forEach { entry ->
      urlBuilder.addEncodedQueryParameter(entry.key, entry.value.toString())
    }
    val request = Request.Builder()
      .get()
      .tag(params)
      .url(urlBuilder.build())
      .cacheControl(CacheControl.FORCE_NETWORK)
      .build()
    val newCall = mClient.newCall(request)
    // 存储请求，用于取消
    cacheCall.put(request.tag(), newCall)
    newCall.enqueue(object : Callback {
      override fun onFailure(call: Call, e: IOException) {
        callback.onFailed(e.message ?: "")
      }

      override fun onResponse(call: Call, response: Response) {
        callback.onSuccess(response.body?.toString() ?: "")
      }
    })
  }

  /**
   * post异步请求
   * @param body Any                 请求参数
   * @param url String               请求地址
   * @param callback IHttpCallback   请求回调
   */
  override fun post(body: Any, url: String, callback: IHttpCallback) {
//    val reqBody = Gson().toJson(body).toRequestBody("application/json".toMediaType())
    val request = Request.Builder()
      .post(Gson().toJson(body).toRequestBody())
      .url("https://testapi.cniao5.com/accounts/login")
      .tag(body)
      .build()

    val newCall = mClient.newCall(request)
    // 存储请求，用于取消
    cacheCall.put(request.tag(), newCall)
    newCall.enqueue(object : Callback {
      override fun onFailure(call: Call, e: IOException) {
        callback.onFailed(e.message ?: "")
      }

      override fun onResponse(call: Call, response: Response) {
        callback.onSuccess(response.body?.string() ?: "")
      }

    })
  }

  /**
   * 取消请求
   * @param tag Any    通过 tag取消
   */
  override fun cancelCall(tag: Any) {
/*    val call = cacheCall[tag]
    call?.apply {
      cancel()
      cacheCall.remove(tag)
    }*/
    cacheCall.remove(tag)?.cancel()
  }

  /**
   * 取消所有请求
   */
  override fun cancelAllCall() {
    for (i in 0 until cacheCall.size()) {
      cacheCall.remove(cacheCall.keyAt(i))?.cancel()
    }
  }

  /**
   * Call 的扩展函数 扩展 okHttp的Call的异步执行方式，结合coroutines，返回DataResult的数据响应
   * @receiver Call
   * @param async Boolean 默认是异步调用，可选参数，false的话就是同步调用
   * @return Response  也可以使用resumeWith返回的是Result<T>
   */
  @OptIn(ExperimentalCoroutinesApi::class)
  private suspend fun Call.call(async: Boolean = true): Response {
    return suspendCancellableCoroutine {
      if (async) {
        enqueue(object : Callback {
          override fun onFailure(call: Call, e: IOException) {
            if (it.isCancelled) return
            it.resumeWithException(e)
          }

          override fun onResponse(call: Call, response: Response) {
            it.resume(response)
          }
        })
      } else {
        it.resume(execute())
      }
      it.invokeOnCancellation {
        try {
          cancel()
        } catch (e: Exception) {
          e.printStackTrace()
        }
      }
    }
  }
}